#include <stdio.h>

#include "esp_log.h"
#include "esp_wifi.h"

#include "nvs_flash.h"
#include "driver/gpio.h"
#include "driver/rmt.h"

#include "iot_button.h"
#include "espnow.h"
#include "led_strip.h"

static const char *TAG = "colortest";

uint16_t hsv[] = {100, 80, 50};

led_strip_t *strip;

void HSVtoRGB(uint8_t *r, uint8_t *g, uint8_t *b, uint16_t h, uint16_t s, uint16_t v)
{
    // R,G,B from 0-255, H from 0-360, S,V from 0-100
    int i;
    float RGB_min, RGB_max;
    RGB_max = v * 2.55f;
    RGB_min = RGB_max * (100 - s) / 100.0f;

    i = h / 60;
    int difs = h % 60; // factorial part of h

    // RGB adjustment amount by hue
    float RGB_Adj = (RGB_max - RGB_min) * difs / 60.0f;

    switch (i)
    {
    case 0:
        *r = RGB_max;
        *g = RGB_min + RGB_Adj;
        *b = RGB_min;
        break;
    case 1:
        *r = RGB_max - RGB_Adj;
        *g = RGB_max;
        *b = RGB_min;
        break;
    case 2:
        *r = RGB_min;
        *g = RGB_max;
        *b = RGB_min + RGB_Adj;
        break;
    case 3:
        *r = RGB_min;
        *g = RGB_max - RGB_Adj;
        *b = RGB_max;
        break;
    case 4:
        *r = RGB_min + RGB_Adj;
        *g = RGB_min;
        *b = RGB_max;
        break;
    default: // case 5:
        *r = RGB_max;
        *g = RGB_min;
        *b = RGB_max - RGB_Adj;
        break;
    }
}

static void button_single_click_cb(void *arg)
{
    ESP_LOGI(TAG, "BUTTON_SINGLE_CLICK");
    hsv[0]++;
    if (hsv[0] > 360)
    {
        hsv[0] = 0;
    }
    ESP_LOGW(TAG, "index: %d", hsv[0]);
    uint8_t r, g, b;
    HSVtoRGB(&r, &g, &b, hsv[0], hsv[1], hsv[2]);

    ESP_ERROR_CHECK(strip->set_pixel(strip, 0, r, g, b));
    ESP_ERROR_CHECK(strip->set_pixel(strip, 1, r, g, b));
    ESP_ERROR_CHECK(strip->refresh(strip, 100));
}

static void button_long_press_cb(void *arg)
{
    ESP_LOGI(TAG, "BUTTON_LONG_PRESS_START");

    espnow_frame_head_t frame_head = {
        .retransmit_count = 5,
        .broadcast = true,
    };

    espnow_send(ESPNOW_TYPE_DATA, ESPNOW_ADDR_BROADCAST, hsv, sizeof(hsv), &frame_head, portMAX_DELAY);
}

static void uart_write_task(void *arg)
{
    esp_err_t ret = ESP_OK;

    uint16_t data[3];
    size_t size = sizeof(data);
    uint8_t addr[ESPNOW_ADDR_LEN] = {0};
    wifi_pkt_rx_ctrl_t rx_ctrl = {0};

    ESP_LOGI(TAG, "Uart write task is running");

    for (;;)
    {
        ret = espnow_recv(ESPNOW_TYPE_DATA, addr, data, &size, &rx_ctrl, portMAX_DELAY);
        ESP_ERROR_CONTINUE(ret != ESP_OK, "<%s>", esp_err_to_name(ret));

        ESP_LOGI(TAG, "espnow_recv, <%d> [" MACSTR "][%d][%d][%d]: %.*s",
                 0, MAC2STR(addr), rx_ctrl.channel, rx_ctrl.rssi, size, size, data);
        // uart_write_bytes(CONFIG_UART_PORT_NUM, data, size);
        hsv[0] = data[0];
        hsv[1] = data[1];
        hsv[2] = data[2];
        ESP_LOGW(TAG, "index: %d", hsv[0]);

        uint8_t r, g, b;
        HSVtoRGB(&r, &g, &b, hsv[0], hsv[1], hsv[2]);

        ESP_ERROR_CHECK(strip->set_pixel(strip, 0, r, g, b));
        ESP_ERROR_CHECK(strip->set_pixel(strip, 1, r, g, b));
        ESP_ERROR_CHECK(strip->refresh(strip, 100));
    }

    ESP_LOGW(TAG, "Uart send task is exit");

    vTaskDelete(NULL);
}

static void wifi_init()
{
    ESP_ERROR_CHECK(esp_netif_init());
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
    ESP_ERROR_CHECK(esp_wifi_start());
}

void app_main(void)
{
    // create gpio button
    button_config_t gpio_btn_cfg = {
        .type = BUTTON_TYPE_GPIO,
        .gpio_button_config = {
            .gpio_num = GPIO_NUM_9,
            .active_level = 0,
        },
    };
    button_handle_t gpio_btn = iot_button_create(&gpio_btn_cfg);
    iot_button_register_cb(gpio_btn, BUTTON_LONG_PRESS_HOLD, button_single_click_cb);
    iot_button_register_cb(gpio_btn, BUTTON_DOUBLE_CLICK, button_long_press_cb);

    rmt_config_t config = RMT_DEFAULT_CONFIG_TX(3, RMT_CHANNEL_0);
    // set counter clock to 40MHz
    config.clk_div = 2;

    ESP_ERROR_CHECK(rmt_config(&config));
    ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

    // install ws2812 driver
    led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(2, (led_strip_dev_t)config.channel);
    strip = led_strip_new_rmt_ws2812(&strip_config);
    if (!strip)
    {
        ESP_LOGE(TAG, "install WS2812 driver failed");
    }
    // Clear LED strip (turn off all LEDs)
    ESP_ERROR_CHECK(strip->clear(strip, 100));

    // nvs init
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    esp_event_loop_create_default();

    // wifi init
    wifi_init();

    // esp now
    espnow_config_t espnow_config = ESPNOW_INIT_CONFIG_DEFAULT();
    espnow_config.qsize.data = 64;
    espnow_init(&espnow_config);

    xTaskCreate(uart_write_task, "uart_write_task", 4 * 1024, NULL, tskIDLE_PRIORITY + 1, NULL);
}
